//
//  BCBaseHttpEngine.m
//  BCBaseNetworking
//
//  Created by Basic on 15/5/5.
//  Copyright (c) 2015年 &#24555;&#26234;&#38598;&#22242;. All rights reserved.
//  网络请求管理
//

#import "BCBaseHttpEngine.h"
#import <BCFoundation/NSString+BCHelper.h>

const int BCBaseRequest_KOperationQueueMaxConcurrentCount =   20;  //请求队列最大并发数

@interface BCBaseHttpEngine ()
@property (nonatomic, strong) NSMutableArray *requestArray;                   /**< 请求队列列表 */
@property (nonatomic, strong) NSURLSessionConfiguration *sessionConfig;        /**< Session配置 */
@end

@implementation BCBaseHttpEngine
#pragma mark - system
static BCBaseHttpEngine * BCBaseHttpEngineSharedInstance = nil;
+ (instancetype)sharedInstance
{
//    static BCBaseHttpEngine * BCBaseHttpEngineSharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        BCBaseHttpEngineSharedInstance = [[[self class] alloc] init];
    });
    return BCBaseHttpEngineSharedInstance;
}

- (id)init
{
    self = [super init];
    if (self) {
        _requestArray = [[NSMutableArray alloc] initWithCapacity:0];
        //init operateQueue
        _httpSerialQueue = [[NSOperationQueue alloc] init];
        [_httpSerialQueue setMaxConcurrentOperationCount:1];
        _httpConcurrentQueue = [[NSOperationQueue alloc] init];
        [_httpConcurrentQueue setMaxConcurrentOperationCount:BCBaseRequest_KOperationQueueMaxConcurrentCount];
        _msgQueue = [[NSOperationQueue alloc] init];
        [_msgQueue setMaxConcurrentOperationCount:1];

        _sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
        _bcSessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:_sessionConfig];
    }
    return self;
}

- (void)destory
{
    BCBaseHttpEngineSharedInstance = nil;
}

- (void)dealloc
{
    _requestArray = nil;
    _httpSerialQueue = nil;
    _httpConcurrentQueue = nil;
    _msgQueue = nil;
    _bcSessionManager = nil;
}

#pragma mark - 发送请求,子类可以override
+ (BCBaseRequestOperation *)bc_sendRequestWithURL:(NSString *)url withApi:(NSString *)api withParam:(NSMutableDictionary *)param withMethod:(BCBaseRequestMethod)method withConstruct:(void (^)(BCHttpRequestModel *requestModel))constructAction withSuccess:(void (^)(BCBaseRequestOperation *request, BCHttpResponseModel *response))successAction withError:(void (^)(BCBaseRequestOperation *request, BCHttpResponseModel *response))errorAction withComplate:(void (^)(BCHttpRequestModel *, BCHttpResponseModel *))complate withTarget:(id)target {
    //构造 RequestModel
    BCHttpRequestModel *reqModel = [[BCHttpRequestModel alloc] initRequestWithDomain:nil withParam:param withMethod:method withSuccess:successAction withError:errorAction withTarget:target];
    reqModel.requestURL = url;
    reqModel.pacTag = api;
    reqModel.queueType = BCRequestQueueConcurrent;
    reqModel.bcComplateBlock = complate;
    if (constructAction) {
        constructAction(reqModel);
    }
    BCBaseRequestOperation *request = [BCBaseHttpEngineInstance bc_addBaseRequest:reqModel];
    return request;
}

#pragma mark - 根据 requestModel 生成 BaseRequest 添加到队列中
- (BCBaseRequestOperation *)bc_addBaseRequest:(BCHttpRequestModel *)requestModel
{
    __block BCBaseRequestOperation *request = nil;
    if ([NSThread isMainThread]) {
        request = [self bc_addBaseRequestAtMainThread:requestModel];
    } else {
        dispatch_async(dispatch_get_main_queue(), ^{
            request = [self bc_addBaseRequestAtMainThread:requestModel];
        });
    }
    return request;
}

- (BCBaseRequestOperation *)bc_addBaseRequestAtMainThread:(BCHttpRequestModel *)requestModel
{
    BCBaseRequestOperation *request = nil;
    //拦截处理RequestAPI 参数
    [self bc_configRequestParams:requestModel];
    //根据业务类型，创建request operation
    request = [self loadRequestOperation:requestModel];
    [_requestArray addObject:request];
    switch (requestModel.queueType) {
        case BCRequestQueueSerial: {
            [self.httpSerialQueue addOperation:request];
            break;
        }
        case BCRequestQueueConcurrent: {
            [self.httpConcurrentQueue addOperation:request];
            break;
        }
        case BCRequestQueueMsg: {
            [self.msgQueue addOperation:request];
            break;
        }
        default:
            break;
    }
    return request;
}

- (BCBaseRequestOperation *)loadRequestOperation:(BCHttpRequestModel *)requestModel
{
    BCBaseRequestOperation *operationReq = [BCBaseRequestOperation new];
    //create session task
    __block NSURLSessionTask *dataTask = nil;
    dataTask = [self setupSessionTask:requestModel withOperation:operationReq];

    //init queue
    operationReq.pacSessionTask = dataTask;
    operationReq.requestModel = requestModel;
    if (!operationReq) {
        BCHttpResponseModel *responseModel = [[BCHttpResponseModel alloc] init];
        responseModel.bc_statusCode = BCBASENETWORK_ERR_UNKNOW;
        requestModel.actionRequestError(operationReq, responseModel);
    }
    return operationReq;
}

/**
 创建一个session task

 @param requestModel 请求数据模型
 @return NSURLSessionTask
 */
- (NSURLSessionTask *)setupSessionTask:(BCHttpRequestModel *)requestModel withOperation:(BCBaseRequestOperation *)operationReq
{
    //init timeoutInterval
    if (requestModel.timeoutInterval <= 0) {
        requestModel.timeoutInterval = 30;
    }
    //create session task
    NSURLSessionTask *dataTask = nil;
    NSMutableURLRequest *urlReq = nil;
    switch (requestModel.requestMethod) {
        case BCBaseRequestMethodPost://post
        {
            urlReq = [self setupRequestWithMethod:@"POST" path:requestModel.finalURL parameters:requestModel.serverPostParams body:requestModel.httpBody];
        }
        break;
        case BCBaseRequestMethodDelete://DELETE
        {
            urlReq = [self setupRequestWithMethod:@"DELETE" path:requestModel.finalURL parameters:requestModel.serverPostParams body:requestModel.httpBody];
        }
        break;
        case BCBaseRequestMethodFileUpload://file
        {
            urlReq = [self loadMultiFormRequestWithMethod:@"POST" path:requestModel.finalURL parameters:requestModel.serverPostParams constructingBodyWithBlock:requestModel.constructingBodyBlock];
            urlReq.HTTPMethod = @"POST";
        }
        break;
        case BCBaseRequestMethodGet://get
       {
            urlReq = [self setupRequestWithMethod:@"GET" path:requestModel.finalURL parameters:requestModel.serverPostParams body:requestModel.httpBody];
        }
        break;
        case BCBaseRequestMethodPUT://get
         {
            urlReq = [self setupRequestWithMethod:@"PUT" path:requestModel.finalURL parameters:requestModel.serverPostParams body:requestModel.httpBody];
        }
        default:
        break;
    }
    [urlReq setTimeoutInterval:requestModel.timeoutInterval];
    //设置cookies,URL encode
    if (requestModel.addUserCookies && requestModel.cookieParams.allKeys.count > 0) {
        for (NSString *key in requestModel.cookieParams.allKeys) {
            id cookieValue = requestModel.cookieParams[key];
            if ([cookieValue isKindOfClass:[NSString class]]) {
                cookieValue = [cookieValue bc_encode];
            }
            NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
            cookieProperties[NSHTTPCookieName] = key;
            cookieProperties[NSHTTPCookieValue] = cookieValue;
            cookieProperties[NSHTTPCookieDomain] = [[urlReq URL] host];
            cookieProperties[NSHTTPCookiePath] = @"/";
            NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
            [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
        }
        //          //会覆盖系统 session id字段，不使用
        //        NSMutableString *cookieValue = [NSMutableString stringWithFormat:@""];
        //        for (NSString *key in requestModel.cookieParams.allKeys) {
        //            NSString *appendString = [NSString stringWithFormat:@"%@=%@;", key, [requestModel.cookieParams[key] bc_encode]];
        //            [cookieValue appendString:appendString];
        //        }
        //        [urlReq addValue:cookieValue forHTTPHeaderField:@"Cookie"];
    }

    // 设置header
    for (NSString *headerKey in requestModel.headerParams) {
        NSString *headerValue = requestModel.headerParams[headerKey];
        [urlReq setValue:headerValue forHTTPHeaderField:headerKey];
    }
    if (requestModel.customHeaders != nil) {
        for (NSString *headerKey in requestModel.customHeaders) {
            NSString *headerValue = requestModel.customHeaders[headerKey];
            [urlReq setValue:headerValue forHTTPHeaderField:headerKey];
        }
    }
    switch (requestModel.requestMethod) {
        case BCBaseRequestMethodGet://get
        case BCBaseRequestMethodPost://post
        case BCBaseRequestMethodPUT://post
        case BCBaseRequestMethodDelete://delete
        default: {
            __weak typeof(self) weakSelf = self;
            dataTask = [self dataTaskWithRequest:urlReq completionHandler:^(NSURLResponse *__unused response, id responseObject, NSError *error) {
                __strong typeof(weakSelf) strongSelf = weakSelf;
                if (!strongSelf) {
                    return;
                }
                [strongSelf bc_onRequestComplate:operationReq withResponse:response withResponseData:responseObject withError:error];
            }];
        }
        break;
        case BCBaseRequestMethodFileUpload://file
        {
            __weak typeof(self) weakSelf = self;
            dataTask = [self.bcSessionManager uploadTaskWithStreamedRequest:urlReq progress:nil completionHandler:^(NSURLResponse *_Nonnull response, id _Nullable responseObject, NSError *_Nullable error) {
                __strong typeof(weakSelf) strongSelf = weakSelf;
                if (!strongSelf) {
                    return;
                }
                if (!strongSelf) {
                    return;
                }
                [strongSelf bc_onRequestComplate:operationReq withResponse:response withResponseData:responseObject withError:error];
            }];
        }
        break;
    }
    return dataTask;
}

/// 配置 Request
/// @param method 请求方法
/// @param path 路径
/// @param parameters 参数
/// @param body body description
- (NSMutableURLRequest *)setupRequestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters body:(id)body {
    NSMutableURLRequest *request = [self loadFormRequestWithMethod:method path:path parameters:parameters];
    //add Body data
    if (body) {
        [request setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]];
    }
    //设置默认http head
    [request setValue:@"no-cache" forHTTPHeaderField:@"Pragma"];
    [request setValue:@"no-cache" forHTTPHeaderField:@"Cache-Control"];
    return request;
}

#pragma mark - 请求回调

/**
 *  请求完成回调
 *
 *  @param request      request对象
 *  @param response     response 对象
 *  @param responseData response数据
 *  @param error        错误信息
 */
- (void)bc_onRequestComplate:(BCBaseRequestOperation *)request withResponse:(NSURLResponse *)response withResponseData:(id)responseData withError:(NSError *)error
{
    [_requestArray removeObject:request];
    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
    if (!request.requestModel || (response && ![response isKindOfClass:[NSHTTPURLResponse class]])) {
        //判断数据不完整
        NSDictionary *responseOld = [error userInfo];
        NSMutableDictionary *responseNew = [[NSMutableDictionary alloc]init];
        if (responseOld != nil) {
            [responseNew addEntriesFromDictionary:responseOld];
        }
        request.responseModel = [BCHttpResponseModel yy_modelWithDictionary:responseNew];
        request.responseModel.bc_statusCode = BCBASENETWORK_STATUS_RCODE_ERROR;
        request.responseModel.bc_data = responseNew;
        request.responseModel.message = @"未知错误，请重试";
        [request onRequestOpeationComplate:NO];
        return;
    } else if (error || httpResponse.statusCode != 200) {
        //判断请求出错
        request.responseModel = [[BCHttpResponseModel alloc] init];
        request.responseModel.bc_statusCode = httpResponse.statusCode;
        request.responseModel.bc_error = error;
        request.responseModel.message = @"网络异常，请重试";
        [request onRequestOpeationComplate:NO];
        //请求失败，判断是否需要重试
        if (request.requestModel.retryCountOnError > request.requestModel.retryCount) {
            //需要重试
            request.requestModel.retryCount++;
            [self bc_addBaseRequest:request.requestModel];
        } else {
            //不需要重试
        }
    } else {
        //请求成功
        request.responseModel = [BCHttpResponseModel yy_modelWithDictionary:responseData];
        request.responseModel.bc_statusCode = httpResponse.statusCode;
        request.responseModel.bc_data = responseData;
        [request onRequestOpeationComplate:YES];
    }
}

#pragma mark - 移除请求
- (void)bc_removeRequestWithTag:(id)tag
{
    if (_requestArray.count <= 0) {
        return;
    }
    NSMutableArray *_listDel = [[NSMutableArray alloc]init];
    NSArray *listReqCopy = [NSArray arrayWithArray:_requestArray];
    for (BCBaseRequestOperation *requestItem in listReqCopy) {
        if ([requestItem.requestModel.pacTag compare:tag] == NSOrderedSame) {
            requestItem.requestModel = nil;
            [_listDel addObject:requestItem];
            [requestItem cancel];
        }
    }
    if (_listDel.count > 0) {
        [_requestArray removeObjectsInArray:_listDel];
    }
}

- (void)bc_removeRequest:(id)target
{
    if (_requestArray.count <= 0) {
        return;
    }
    NSMutableArray *_listDel = [[NSMutableArray alloc]init];
    NSArray *listReqCopy = [NSArray arrayWithArray:_requestArray];
    for (BCBaseRequestOperation *requestItem in listReqCopy) {
        if (requestItem.requestModel.targetDelegate == target) {
            requestItem.requestModel = nil;
            [_listDel addObject:requestItem];
            [requestItem cancel];
        }
    }
    if (_listDel.count > 0) {
        [_requestArray removeObjectsInArray:_listDel];
    }
}

#pragma mark - 生成Request
/**
 *  生成 url request
 *
 *  @param method     方法类型
 *  @param path       请求路径
 *  @param parameters 参数
 *
 *  @return NSMutableURLRequest
 */
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters
{
    NSURL *url = [NSURL URLWithString:path relativeToURL:_bcSessionManager.baseURL];
    NSMutableURLRequest *request = [_bcSessionManager.requestSerializer requestWithMethod:method URLString:url.absoluteString parameters:parameters error:nil];
    return request;
}

/**
 生成简单 NSURLRequest

 @param method method description
 @param path path description
 @param parameters parameters description
 @return return value description
 */
- (NSMutableURLRequest *)loadFormRequestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters
{
    NSURL *url = [NSURL URLWithString:path relativeToURL:_bcSessionManager.baseURL];
//    NSMutableURLRequest *request = [_bcSessionManager.requestSerializer multipartFormRequestWithMethod:method URLString:url.absoluteString parameters:parameters constructingBodyWithBlock:nil error:nil];
    NSMutableURLRequest *request = [_bcSessionManager.requestSerializer requestWithMethod:method URLString:url.absoluteString parameters:parameters error:nil];
    return request;
}

/**
 ，包含多部分 NSURLRequest

 @param method method description
 @param path path description
 @param parameters parameters description
 @param block block description
 @return return value description
 */
- (NSMutableURLRequest *)loadMultiFormRequestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
{
    NSURL *url = [NSURL URLWithString:path relativeToURL:_bcSessionManager.baseURL];
    NSMutableURLRequest *request = [_bcSessionManager.requestSerializer multipartFormRequestWithMethod:method URLString:url.absoluteString parameters:parameters constructingBodyWithBlock:block error:nil];
    return request;
}

#pragma mark - 创建Session task
/**
 *  根据Request创建Session task
 *
 *  @param request           NSURLRequest
 *  @param completionHandler 完成回调
 *
 *  @return NSURLSessionDataTask
 */
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
//#pragma clang diagnostic push
//#pragma clang diagnostic ignored "-Wdeprecated-declarations"
//    return [_bcSessionManager dataTaskWithRequest:request completionHandler:completionHandler];
//#pragma clang diagnostic pop
    return [_bcSessionManager dataTaskWithRequest:request uploadProgress:NULL downloadProgress:NULL completionHandler:completionHandler];
}

- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData progress:(void (^)(NSProgress *uploadProgress))uploadProgressBlock completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    return [_bcSessionManager uploadTaskWithRequest:request fromData:bodyData progress:uploadProgressBlock completionHandler:completionHandler];
}

#pragma mark - 参数处理方法

/**
 request 参数重新构造

 @param request request description
 */
- (void)bc_configRequestParams:(BCHttpRequestModel *)request
{
    //子类重写
}

@end
